package com.bitcola.chainserver.chain.trx.core;



import com.bitcola.chainserver.chain.trx.api.*;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class TronApi {
    private NodeClient fullNode;
    private NodeClient solidityNode;
    private NodeClient eventNode;
    private NodeClient dataNode;


    public TronApi(String fullNodeUrl, String solidityNodeUrl, String eventNodeUrl) {
        fullNode = new NodeClient(fullNodeUrl);
        solidityNode = new NodeClient(solidityNodeUrl);
        eventNode = new NodeClient(eventNodeUrl);
        dataNode = new NodeClient("https://apilist.tronscan.org");
    }

    public TronApi() {
        fullNode = new NodeClient("https://api.trongrid.io");
        solidityNode = new NodeClient("https://api.trongrid.io");
        eventNode = new NodeClient("https://api.trongrid.io");
        dataNode = new NodeClient("https://apilist.tronscan.org");

    }

    public TronApi(String url) {
        this(url, url, url);
    }

    public static TronApi mainNet() {
        return new TronApi("https://api.trongrid.io");
    }

    public static TronApi testNet() {
        return new TronApi("https://api.shasta.trongrid.io");
    }

    public TransferTransaction createTransaction(String to, long amount, String owner) throws Exception {
        CreateTransactionRequest req = new CreateTransactionRequest(
                Address.decode(to), amount, Address.decode(owner));
        return fullNode.post(TransferTransaction.class, "wallet/createtransaction", req);
    }

    public Account getAccount(String address) throws Exception {
        AccountRequest req = new AccountRequest(Address.decode(address));
        return fullNode.post(Account.class, "wallet/getaccount", req);
    }


    public static String hexStringToString(String s) {
        if (s == null || s.equals("")) {
            return null;
        }
        s = s.replace(" ", "");
        byte[] baKeyword = new byte[s.length() / 2];
        for (int i = 0; i < baKeyword.length; i++) {
            try {
                baKeyword[i] = (byte) (0xff & Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        try {
            s = new String(baKeyword, "UTF-8");
            new String();
        } catch (Exception e1) {
            e1.printStackTrace();
        }
        return s;
    }

    public ApiResult broadcastTransaction(Object tx) throws Exception {
        ApiResult ret = fullNode.post(ApiResult.class, "wallet/broadcasttransaction", tx);
        if (ret.message != null) {
            ret.message = hexStringToString(ret.message);
        }
        return ret;
    }

    public TriggerContractResponse triggerConstantContract(String contractAddress, String functionSelector, String parameter, String ownerAddress) throws Exception {
        TriggerContractRequest req = new TriggerContractRequest(
                Address.decode(contractAddress), functionSelector, parameter, Address.decode(ownerAddress));
        return fullNode.post(TriggerContractResponse.class, "wallet/triggerconstantcontract", req);
    }

    public TriggerContractResponse triggerSmartContract(String contractAddress, String functionSelector, String parameter, String ownerAddress) throws Exception {
        TriggerContractRequest req = new TriggerContractRequest(
                Address.decode(contractAddress), functionSelector, parameter, Address.decode(ownerAddress));
        return fullNode.post(TriggerContractResponse.class, "wallet/triggersmartcontract", req);
    }

    public List<ContractEvent> getBlockEvents(long block_number) throws Exception {
        String api = String.format("v1/blocks/%d/events", block_number);
        Map params = new HashMap<String, String>();
        params.put("sort", "block_timestamp");
        var result=eventNode.get(ContractEventResult.class, api, params);;
        for (int i = 0; i <result.contractEvents.size() ; i++) {
            var e=result.contractEvents.get(i);
            for(String key:e.result.keySet())
            {
                if(key.equals("_from")||key.equals("_to")||key.equals("from")||key.equals("to")){
                    String hexAddress=e.result.get(key).toString();
                    String address= hexAddress.substring(2,hexAddress.length());

                    address="41"+address;

                    hexAddress="0x"+address;

                    String base58Address=   Address.encode(hexAddress);
                    e.result.put(key,base58Address);
                }
            }
        }

        return result.contractEvents;
    }

    public Long getLastNlock() throws Exception {

        var map = dataNode.get(Map.class, "api/block/latest", new HashMap<>());

        BigDecimal number = new BigDecimal(map.get("number").toString());
        return number.longValue();
    }


    public List<TRXTransform>getTransformsByBlockNumber(long number) throws Exception {
      int pageSize=50;
      int page=1;
      int resultSize=0;
      List<TRXTransform>trxTransforms=new ArrayList<>();
      do {
        String url = String.format("api/tx/trx/%d/%d/%d", number, page, pageSize);
          System.out.println(url);
        var data= dataNode.get(TrxTransactionResult.class, url, new HashMap<>());
        if(data.code==1){
            resultSize=data.trxTransformList.size();
            trxTransforms.addAll( data.trxTransformList.stream().filter(x->x.tType.equals("Transfer")).collect(Collectors.toList()));

        }else {
          break;
        }
        page++;
      }while (resultSize>=pageSize);

      return trxTransforms;
    }


    public Map getTransformById(String hash) throws Exception {
        String url="wallet/gettransactionbyid";
        Map params = new HashMap<String, String>();
        params.put("value", hash);
         return fullNode.post(Map.class,url,params);
    }



    public static void main(String[] args) throws Exception {
        new TronApi().getLastNlock();
    }
}